home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 5
/
Apprentice-Release5.iso
/
Source Code
/
C++
/
Frameworks
/
Sprocket Framework DR2
/
Sprocket Framework
/
DocumentWindow.cp
< prev
next >
Wrap
Text File
|
1996-06-12
|
22KB
|
981 lines
/*
File: UDocumentWindow.cp
Project: Sprocket Framework 1.1 of 6/13/96
Contains: Code which handles the standard functions required of a document-based window
To Do: ?
Sprocket Major Contributors:
----------------------------
Dave Falkenburg, producer of Sprocket 1.0
Bill Hayden, producer of Sprocket 1.1
Steve Sisak, producer of the upcoming Sprocket 2.0
Pete Alexander Steve Falkenburg Randy Thelen
Eric Berdahl Nitin Ganatra Chris K. Thomas
Marshall Clow Dave Hershey Leonard Rosenthal
Tim Craycroft Dave Mark Dean Yu
David denBoer Gary Powell
Cameron Esfahani Jon Summers Apple Computer, Inc.
Comments, Additions, or Corrections:
------------------------------------
Bill Hayden, Nikol Software <nikol@codewell.com>
*/
#include "DocumentWindow.h"
#include "Sprocket.h"
#include "StandardMenus.h"
#include "UDialog.h"
#include "UString.h"
#include "UPrinting.h"
#include <GXExceptions.h>
#include <GXEnvironment.h>
#include <GXGraphics.h>
#include <Printing.h>
#include <Errors.h>
TDynamicArray TDocumentWindow::fgActiveDocuments;
static OSErr ReplaceCollectionItem(void *newData, long collectSize,
OSType collectType, long collectID,
Collection whichCollection,
Ptr *oldData, long *oldDataSize);
static OSErr PrintOneGXShape(gxShape currentShape, long refCon);
static OSErr CheckObjectLock(short vRefNum, long dirID, StringPtr name);
/**********************************************************************************/
TDocumentWindow::TDocumentWindow(FSSpecPtr theFile /* = NULL */)
{
OSErr err;
fgActiveDocuments.InsertFirst(this);
if (theFile)
{
fDirty = false;
fHasBeenSaved = true;
fHasNewEditions = false;
fBackingFile = new TFile(*theFile);
}
else
{
fDirty = true;
fHasBeenSaved = false;
fHasNewEditions = false;
}
pcpy(fDocumentKind, "\p");
pcpy(fDocumentName, "\pUntitled");
if (gHasQuickDrawGX)
{
err = GXNewJob(&fPrintJob);
if (err == noErr)
GXInstallApplicationOverride(fPrintJob,
gxPrintingEventMsg,
NewGXPrintingEventProc(PrintingEventOverrideForGX));
fPageFormats = new TDynamicArray;
}
else
{
fPrintRecord = (THPrint) NewHandle(sizeof(TPrint));
err = MemError();
if (!err)
{
PrOpen();
PrintDefault(fPrintRecord);
PrClose();
}
}
if (err)
{
fCanPrint = false;
ErrorReporter(err, __FILE__, __LINE__);
}
else
fCanPrint = true;
}
/**********************************************************************************/
TDocumentWindow::~TDocumentWindow()
{
fgActiveDocuments.FindAndDeleteElement(this);
}
/**********************************************************************************/
Boolean TDocumentWindow::DoCommand(CommandID command)
{
switch (command)
{
case cSave:
if (fHasBeenSaved)
this->Save();
else
this->SaveAs();
break;
case cSaveAs:
this->SaveAs();
break;
case cPageSetup:
case cCustomPageSetup:
this->PageSetup(command == cCustomPageSetup);
break;
case cPrint:
case cPrintOne:
this->Print(command == cPrint);
break;
default:
return false;
}
return true;
}
/**********************************************************************************/
Boolean TDocumentWindow::Close(void)
{
return TWindow::Close();
}
/**********************************************************************************/
Boolean TDocumentWindow::CanClose(Boolean quitting)
{
SetCursor(&qd.arrow); // in case the user hit cmd-W while over the text area
if (fDirty)
{
Str255 actionStr;
short reply, SaveAlertIndex;
OSErr err;
GetIndString(actionStr, kStandardCloseStrings, quitting ? kQuittingStr : kClosingStr);
ParamText(this->fDocumentKind, this->fDocumentName, actionStr, "\p");
if (fHasNewEditions)
SaveAlertIndex = kStandardCloseWithNewPubsAlertID;
else
SaveAlertIndex = kStandardCloseAlertID;
reply = StandardAlert(SaveAlertIndex, 1, 2);
switch (reply)
{
case 3: // Don't Save
return true;
break;
case 2: // Cancel
return false;
break;
case 1: // OK (i.e. save the document)
if (fHasBeenSaved)
err = this->Save();
else
err = this->SaveAs();
ErrorReporter(err, __FILE__, __LINE__);
break;
}
}
return true;
}
/**********************************************************************************/
Boolean TDocumentWindow::DeleteAfterClose(void)
{
if (fPrintRecord != nil)
DisposeHandle((Handle) fPrintRecord);
if (fPrintJob != nil)
GXDisposeJob(fPrintJob);
return true;
}
/**********************************************************************************/
Boolean TDocumentWindow::CloseAllDocuments(Boolean quitting)
{
TDocumentWindow *aDocument;
aDocument = (TDocumentWindow *) fgActiveDocuments.GetIndexedElement(0);
while (aDocument != nil)
{
if (aDocument->CanClose(quitting))
aDocument->Close();
else
break;
aDocument = (TDocumentWindow *) fgActiveDocuments.GetIndexedElement(0);
}
// If we got through all the documents, return true
return (aDocument == NULL);
}
/**********************************************************************************/
void TDocumentWindow::Activate(Boolean activating)
{
if (activating)
fgActiveDocuments.MoveToFront((ArrayElementPtr) this);
}
/**********************************************************************************/
#pragma mark === Filing Methods ===
OSErr TDocumentWindow::Save()
{
return -1;
}
/**********************************************************************************/
OSErr TDocumentWindow::SaveAs()
{
FInfo fileInfo;
StandardFileReply reply;
OSErr err;
unsigned long theTime;
short dataForkRefNum = 0;
short resForkRefNum = 0;
Str255 tempFileName;
FSSpec tempFileSpec;
short tempVRef; // volume reference # for the temp file
long tempDirID; // directory ID of the temp file
TFile* tempFile;
StandardPutFile("\pSave this document as:", this->fDocumentName, &reply);
if (!reply.sfGood)
return userCanceledErr;
if ( reply.sfReplacing )
{
err = CheckObjectLock(reply.sfFile.vRefNum, reply.sfFile.parID, (StringPtr) reply.sfFile.name);
if ( err != noErr ) // if it returns noErr, the file/directory is NOT locked
return err;
// create the temporary file name. the name doesn't have to make sense, just
// be unique
GetDateTime( &theTime );
NumToString( theTime, tempFileName );
// find the temporary items folder on the file's volume; create it if necessary
// it is important that the temp folder (and the temp file) and the "original" target
// file be on the same volume; if not, FSpExchangeFiles will return diffVolErr (-1303)
// and won't work
err = FindFolder( reply.sfFile.vRefNum, kTemporaryFolderType, kCreateFolder, &tempVRef, &tempDirID );
if ( err != noErr )
return err; // for now, do nothing, just return
// make an FSSpec for the temp file
err = FSMakeFSSpec( tempVRef, tempDirID, tempFileName, &tempFileSpec );
if ( (err != noErr) && (err != fnfErr ) )
return err;
tempFile = new TFile(tempFileSpec);
err = tempFile->CreateNewFile('trsh', 'trsh', smSystemScript );
err = FSpSetFInfo( &tempFileSpec, &fileInfo );
if ( err != noErr )
{
goto cleanup;
}
dataForkRefNum = tempFile->OpenDataFork(fsWrPerm);
}
else
{
fBackingFile->SetSpecifier(reply.sfFile);
err = fBackingFile->CreateNewFile('75SL', 'TEXT', smSystemScript );
dataForkRefNum = fBackingFile->OpenDataFork(fsWrPerm);
}
err = SetEOF( dataForkRefNum, 0 );
if ( err != noErr )
goto cleanup;
// Write your file’s data into the data fork
err = WriteDocumentDataFork();
if (reply.sfReplacing)
err = tempFile->CloseDataFork();
else
err = fBackingFile->CloseDataFork();
if ( err != noErr )
goto cleanup;
if ( reply.sfReplacing )
resForkRefNum = tempFile->OpenResourceFork( fsWrPerm );
else
resForkRefNum = fBackingFile->OpenResourceFork( fsWrPerm );
err = ResError();
if ( err != noErr )
goto cleanup;
// Write your file’s resources into the resource fork
err = WriteDocumentResourceFork();
if ( reply.sfReplacing )
err = tempFile->CloseResourceFork();
else
err = fBackingFile->CloseResourceFork();
if ( err != noErr )
goto cleanup;
if ( reply.sfReplacing )
{
// Everything is written, so swap the files
err = FSpExchangeFiles( &tempFileSpec, &reply.sfFile );
if ( err != noErr )
goto cleanup;
// can the temp file since we don't need it anymore
tempFile->SetSpecifier(tempFileSpec);
err = tempFile->Delete();
if ( err != noErr )
goto cleanup;
}
// and update the disk with any unwritten data
FlushVol( "\p", reply.sfFile.vRefNum );
pcpy(fDocumentName, reply.sfFile.name);
SetWTitle(fWindow, fDocumentName);
cleanup:
if (tempFile)
delete tempFile;
return err;
}
/**********************************************************************************/
OSErr TDocumentWindow::Revert()
{
return -1;
}
/**********************************************************************************/
#pragma mark === Printing Methods ===
void TDocumentWindow::PageSetup(Boolean useCustomPageSetup)
{
Boolean result;
gxEditMenuRecord editMenuRec;
gxFormat pageFormat;
Boolean newPgFormat = false;
OSErr err;
if (gHasQuickDrawGX) // QuickDraw GX is being used.
{
// Fill in the location of the application’s Edit menu items
// and enable the appropriate menu items.
editMenuRec.editMenuID = mEdit;
editMenuRec.cutItem = iCut;
editMenuRec.copyItem = iCopy;
editMenuRec.pasteItem = iPaste;
editMenuRec.clearItem = iClear;
editMenuRec.undoItem = iUndo;
gMenuBar->HiliteMenusForModalDialog(false);
if (useCustomPageSetup)
{
// If we have an existing page format, we'll modify that.
// Otherwise, we'll need to create a new format and use that.
if (fPageFormats->GetIndexedElement(fCurrentPage - 1) != nil)
pageFormat = (gxFormat)fPageFormats->GetIndexedElement(fCurrentPage - 1);
else
{
pageFormat = GXNewFormat(fPrintJob);
newPgFormat = true;
err = GXGetJobError(fPrintJob);
}
// If no errors, display the format dialog.
if (err == noErr)
{
result = GXFormatDialog(pageFormat, &editMenuRec, nil);
switch (result)
{
/* If the user selected "Remove", use the default job format
with this page. For our application, we indicate this by
storing nil for the format reference in our structure.
If "OK" was selected, store the new format with this page.
*/
case gxRevertSelected:
GXDisposeFormat(pageFormat);
pageFormat = nil;
case gxOKSelected:
fPageFormats->SetIndexedElement(fCurrentPage - 1, (ArrayElementPtr)pageFormat);
break;
// If the user selected "Cancel", dispose of our cloned copy
// of the default job format, if we made one.
case gxCancelSelected:
if (newPgFormat)
GXDisposeFormat(pageFormat);
break;
}
}
}
else
{
GXJobDefaultFormatDialog(fPrintJob, &editMenuRec);
}
gMenuBar->HiliteMenusForModalDialog(true);
}
else // QuickDraw GX is not being used.
{
PrOpen();
PrValidate(fPrintRecord);
PrStlDialog(fPrintRecord);
PrClose();
}
}
/**********************************************************************************/
OSErr TDocumentWindow::Print(Boolean usePrintJobDialog)
{
OSErr err = noErr;
gxEditMenuRecord editMenuRec;
gxDialogResult result;
if (!fCanPrint)
return -1;
if (gHasQuickDrawGX)
{
if (usePrintJobDialog)
{
/*
If QuickDraw GX is present, fill in the location of the application's
Edit menu items, enable/disable appropriate menu items, and display
the Print dialog. If the user clicks the Print button, print using
QuickDraw GX.
*/
editMenuRec.editMenuID = mEdit;
editMenuRec.cutItem = iCut;
editMenuRec.copyItem = iCopy;
editMenuRec.pasteItem = iPaste;
editMenuRec.clearItem = iClear;
editMenuRec.undoItem = iUndo;
gMenuBar->HiliteMenusForModalDialog(false);
result = GXJobPrintDialog(fPrintJob, &editMenuRec);
gMenuBar->HiliteMenusForModalDialog(true);
if (result == gxOKSelected)
err = GXPrintLoop(); // Print using QuickDraw GX
}
else // Print One
{
OSErr err;
Collection jobCollection;
gxCopiesInfo copiesInfo;
gxFileDestinationInfo destInfo;
gxPageRangeInfo pageRangeInfo;
Ptr oldCopiesInfo = nil, oldPageRangeInfo = nil, oldDestInfo = nil;
long oldCopiesSize, oldPageRangeInfoSize, oldDestInfoSize;
// Get the job collection and set it up to print one copy…
jobCollection = GXGetJobCollection(fPrintJob);
// Set number of copies to 1.
copiesInfo.copies = 1;
err = ReplaceCollectionItem(&copiesInfo, sizeof(gxCopiesInfo),
gxCopiesTag, gxPrintingTagID,
jobCollection, &oldCopiesInfo, &oldCopiesSize);
nrequire(err, ReplaceCopies_error);
// Set page range to "all".
pageRangeInfo.simpleRange.optionChosen = gxDefaultPageRange;
pageRangeInfo.minFromPage = 1;
pageRangeInfo.simpleRange.fromPage = 1;
pageRangeInfo.maxToPage = fNumOfPages;
pageRangeInfo.simpleRange.toPage = fNumOfPages;
pageRangeInfo.simpleRange.printAll = true;
err = ReplaceCollectionItem(&pageRangeInfo, sizeof(gxPageRangeInfo),
gxPageRangeTag, gxPrintingTagID,
jobCollection, &oldPageRangeInfo, &oldPageRangeInfoSize);
nrequire(err, ReplacePageRange_error);
// Set destination to "printer".
destInfo.toFile = false;
err = ReplaceCollectionItem(&destInfo, sizeof(gxFileDestinationInfo),
gxFileDestinationTag, gxPrintingTagID,
jobCollection, &oldDestInfo, &oldDestInfoSize);
nrequire(err, ReplaceDestination_error);
// Print one copy of our document.
err = GXPrintLoop();
/*
Restore original number of copies, page range, and output
destination in case anybody uses that info.
*/
ReplaceDestination_error:
ReplaceCollectionItem(oldDestInfo, oldDestInfoSize,
gxFileDestinationTag, gxPrintingTagID,
jobCollection, nil, nil);
ReplacePageRange_error:
ReplaceCollectionItem(oldPageRangeInfo, oldPageRangeInfoSize,
gxPageRangeTag, gxPrintingTagID,
jobCollection, nil, nil);
ReplaceCopies_error:
ReplaceCollectionItem(oldCopiesInfo, oldCopiesSize,
gxCopiesTag, gxPrintingTagID,
jobCollection, nil, nil);
// Dispose of the pointers that MyReplaceCollectionItem created.
if (oldCopiesInfo)
DisposePtr(oldCopiesInfo);
if (oldPageRangeInfo)
DisposePtr(oldPageRangeInfo);
if (oldDestInfo)
DisposePtr(oldDestInfo);
}
}
else
{
// If QuickDraw GX is NOT present, open the printer driver, and put up the
// old Print dialog. If the user clicks the Print button, print using
// the Printing Manager.
PrOpen();
PrValidate(fPrintRecord);
if (PrJobDialog(fPrintRecord))
err = QDPrintLoop(); // Print using QuickDraw
PrClose();
}
return err;
}
/**********************************************************************************/
OSErr TDocumentWindow::QDPrintLoop(void)
{
OSErr err = noErr;
TPPrPort printPort;
TPPrStatusRef theStatus;
short copy, pg, iFstPage, iLstPage, oldCurPage;
oldCurPage = fCurrentPage;
iFstPage = (*fPrintRecord)->prJob.iFstPage;
if (iFstPage < 1)
iFstPage = 1;
iLstPage = (*fPrintRecord)->prJob.iLstPage;
if (iLstPage > fNumOfPages)
iLstPage = fNumOfPages;
(*fPrintRecord)->prJob.iFstPage = 1;
(*fPrintRecord)->prJob.iLstPage = 9999;
// Loop for the number of copies we need to make, opening a
// document and a page for each.
for (copy = 1; !err && (copy <= (*fPrintRecord)->prJob.iCopies); copy++)
{
printPort = PrOpenDoc(fPrintRecord, nil, nil);
if (!(err = PrError()))
for (pg = iFstPage; pg <= iLstPage; pg++)
{
PrOpenPage(printPort, nil);
fCurrentPage = pg;
DrawCurrentPage();
PrClosePage(printPort);
}
PrCloseDoc(printPort);
}
// When finished printing, call PrPicFile (if necessary).
if (!err)
err = PrError();
if (!err && ((*fPrintRecord)->prJob.bJDocLoop == bSpoolLoop))
PrPicFile(fPrintRecord, nil, nil, nil, theStatus);
fCurrentPage = oldCurPage;
return err;
}
/**********************************************************************************/
OSErr TDocumentWindow::GXPrintLoop(void)
{
OSErr err;
long firstPage, lastPage, numPages, pg;
long oldPage;
gxViewPort printViewPort;
Point patStretch = {1,1};
gxFormat pageFormat;
Rect everywhereRect;
GXPrintData spoolData;
oldPage = fCurrentPage;
// Determine which pages the user selected to print. If the user specifies
// a page range that is greater than the actual number of pages in the document,
// only print those pages that are actually in the document.
GXGetJobPageRange(fPrintJob, &firstPage, &lastPage);
if (lastPage > fNumOfPages)
lastPage = fNumOfPages;
// Calculate the total number of pages to print, and begin
// printing if there are no errors.
numPages = lastPage - firstPage +1;
err = GXGetJobError(fPrintJob);
nrequire(err, PageRangeError);
GXStartJob(fPrintJob, fDocumentName, numPages);
err = GXGetJobError(fPrintJob);
nrequire(err, StartJobFailed);
// Create a new viewport for printing, and set our translator rects
// to "wide open" so they include all data we're drawing. For each
// page we print, call GXStartPage, draw, and call GXFinishPage.
SetRect(&everywhereRect, 0, 0, 32767, 32767);
printViewPort = GXNewViewPort(gxScreenViewDevices);
for (pg = firstPage; (err == noErr) && (pg <= lastPage); pg++)
{
// Get the page's format and start printing the page.
pageFormat = (gxFormat)fPageFormats->GetIndexedElement(pg - 1);
if (pageFormat == nil)
pageFormat = GXGetJobFormat(fPrintJob, 1);
GXStartPage(fPrintJob, pg, pageFormat, 1, &printViewPort);
err = GXGetJobError(fPrintJob);
// If there were no errors, set up the Translator, draw the QuickDraw
// data for the current page and remove the Translator.
// GXInstallQDTranslator actually requires a pointer to a CGrafPort,
// and will crash if you pass it a true GrafPtr.
nrequire(err, StartPageFailed);
spoolData.printViewPort = printViewPort;
GXGetFormatDimensions(pageFormat, &spoolData.pageArea, nil);
GXInstallQDTranslator(fWindow,
gxDefaultOptionsTranslation,
&everywhereRect, &everywhereRect,
patStretch,
NewgxShapeSpoolProc(PrintOneGXShape),
&spoolData);
fCurrentPage = pg;
SetPortWindowPort(fWindow);
DrawCurrentPage();
GXRemoveQDTranslator(fWindow, nil);
// Finish the page.
GXFinishPage(fPrintJob);
}
// Finish printing.
StartPageFailed:
GXFinishJob(fPrintJob);
err = GXGetJobError(fPrintJob);
GXDisposeViewPort(printViewPort);
fCurrentPage = oldPage;
StartJobFailed:
PageRangeError:
return err;
}
/**********************************************************************************/
void TDocumentWindow::DrawCurrentPage(void)
{
// override me
}
/**********************************************************************************/
#pragma mark === Static Functions ===
static OSErr ReplaceCollectionItem(void *newData, long collectSize,
OSType collectType, long collectID,
Collection whichCollection,
Ptr *oldData, long *oldDataSize)
{
OSErr err;
long index;
// If we're supposed to return the old data, get it.
// If there is no old data, return a copy of the new data.
if (oldData)
{
err = GetCollectionItemInfo(whichCollection,
collectType,
collectID,
kCollectionDontWantIndex,
oldDataSize,
kCollectionDontWantAttributes);
if (err)
{
*oldDataSize = collectSize;
*oldData = NewPtrSys(*oldDataSize);
if (!(err = MemError()))
BlockMoveData(newData, *oldData, collectSize);
}
else
{
*oldData = NewPtrSys(*oldDataSize);
if (!(err = MemError()))
err = GetCollectionItem(whichCollection,
collectType,
collectID,
kCollectionDontWantSize,
*oldData);
}
nrequire(err, CouldNotSetOldData);
}
// If we're adding a new collection item, do so. Otherwise,
// get the existing item's index and replace the old collection item.
err = AddCollectionItem(whichCollection,
collectType,
collectID,
collectSize,
newData);
if (err == collectionItemLockedErr)
{
err = GetCollectionItemInfo(whichCollection,
collectType,
collectID,
&index,
kCollectionDontWantSize,
kCollectionDontWantAttributes);
if (!err)
err = ReplaceIndexedCollectionItem(whichCollection,
index,
collectSize,
newData);
}
CouldNotSetOldData:
return err;
}
/**********************************************************************************/
static OSErr PrintOneGXShape(gxShape currentShape, long refCon)
{
GXPrintDataPtr DataPtr;
gxShapeType theShapeType;
// Don't waste time spooling the shape if it’s being
// drawn completely off the page.
DataPtr = (GXPrintDataPtr) refCon;
theShapeType = GXGetShapeType(currentShape);
if ((theShapeType == gxEmptyType) || (theShapeType == gxFullType) ||
(theShapeType == gxPictureType) || (GXTouchesBoundsShape(&DataPtr->pageArea, currentShape)))
{
GXSetShapeViewPorts(currentShape, 1, &DataPtr->printViewPort);
GXDrawShape(currentShape);
}
return (OSErr) GXGetGraphicsError(nil);
}
/**********************************************************************************/
static OSErr CheckObjectLock(short vRefNum, long dirID, StringPtr name)
{
CInfoPBRec pb;
OSErr error;
pb.hFileInfo.ioNamePtr = name;
pb.hFileInfo.ioVRefNum = vRefNum;
pb.hFileInfo.ioDirID = dirID;
pb.hFileInfo.ioFDirIndex = 0; // use ioNamePtr and ioDirID
error = PBGetCatInfoSync(&pb);
if ( error == noErr )
{
// check locked bit
if ( (pb.hFileInfo.ioFlAttrib & 0x01) != 0 )
error = fLckdErr;
}
return ( error );
}